package com.ictravi.iamunit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import jxl.*;
import jxl.read.biff.BiffException;
import org.apache.commons.lang.ClassUtils;
import com.ictravi.iamunit.IAMUnitException;
import com.ictravi.iamunit.EncTool;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import com.ictravi.iamunit.connector.ConnectionConfigData;

/**
 * Loads the configuration for the tests
 * 
 * @author Albert Louw (alouw@ictavi.com)
 * @version v1.0.0
 * @since v1.0.0
 **/
public class ConfigLoader {
	// Global Class Variables
	private static Log LOG = LogFactory.getLog(IAMUnitTestCase.class);
	public static Map<String, ConnectionConfigData> cmap = new HashMap<String, ConnectionConfigData>();
	public static Map<String, String> parms = new HashMap<String, String>();
	static Map<String, String> schema = new HashMap<String, String>();
	// Constants
	private final static String XML_NAME = "name";
	private final static String XML_PROFILES = "profiles";
	private final static String XML_PARAMS = "params";
	private final static String XML_TYPE = "type";
	private final static String XML_PASSWORD = "password";
	private final static String XML_LIVE_PROFILE = "live-profile";
	private final static String XML_ENABLE_EMAIL_ALERTS = "enable-email-alerts";
	private final static String XML_ENABLE_LOG_ALERTS = "enable-log-alerts";
	private final static String XML_SUBSTITUTIONS = "substitutions";
	private final static String XML_REPLACE = "replace";
	private final static String XML_NEW = "new";

	/**
	 * loads the config from specified file
	 * 
	 * @since v1.0.0
	 * @param name
	 *            filename to read the config from
	 * @throws IAMUnitException
	 **/
	public static void loadConfig(String name) throws IAMUnitException {
		LOG.info("");
		LOG.info("*****************************************************************************************************************************");
		LOG.info("** IAMUnit v1.0.0 (C) 2011 iCTraVi Holding B.V.                                                                            **");
		LOG.info("*****************************************************************************************************************************");
		LOG.info("");

		LOG.info("...Loading configuration from file.");
		cmap = getConfigData(name);
	}

	/**
	 * sets the schema for a specific target
	 * 
	 * @since v1.0.0
	 * @param name
	 *            target to set schema for
	 * @param schemaMap
	 *            schemaMap to insert for the target
	 * @throws IAMUnitException
	 **/
	public static void setSchema(String name, Map<String, String> schemaMap) {
		schema.put(name, schemaMap.toString());
	}

	/**
	 * Get the schema for a the target in name
	 * 
	 * @since v1.0.0
	 * @param name
	 *            get the schema for a specific target
	 * @return String the schema for this target
	 * @throws IAMUnitException
	 **/
	public static String getSchema(String name) {
		return schema.get(name);
	}

	/**
	 * Loads XML file from filesystem
	 * 
	 * @since v1.0.0
	 * @param fullPath
	 *            filename to read the config from
	 * @throws IAMUnitException
	 **/
	private static Document loadXMLFromFS(String fullPath) throws IAMUnitException {
		SAXBuilder fileSystemDoc = new SAXBuilder();
		Document inputDoc = null;
		try {
			inputDoc = fileSystemDoc.build(new FileReader(fullPath));
		} catch (JDOMException jde) {
			throw new IAMUnitException(
					"IAM00001: Configuration file is invalid. Ensure that the target profile has connections defined according to the idmunit.dtd) - "
							+ jde.getMessage());
		} catch (FileNotFoundException fnfe) {
			throw new IAMUnitException(
					"IAM00002: Configuration not found. Ensure that idmunit-config.xml is in the location specified by idmunit-defaults.properites.");
		}
		return inputDoc;
	}

	/**
	 * gets the connection's configuration data from the xml
	 * 
	 * @since v1.0.0
	 * @param configFileLocation
	 *            filename to read the config from
	 * @throws IAMUnitException
	 **/
	public static Map<String, ConnectionConfigData> getConfigData(String configFileLocation) throws IAMUnitException {
		Map<String, ConnectionConfigData> connectionMap = new HashMap<String, ConnectionConfigData>();
		Document doc = loadXMLFromFS(configFileLocation);

		String liveTarget = doc.getRootElement().getAttributeValue(
				XML_LIVE_PROFILE);

		String enableEmailAlerts = doc.getRootElement().getAttributeValue(
				XML_ENABLE_EMAIL_ALERTS);
		if (enableEmailAlerts == null)
			enableEmailAlerts = "false";

		String enableLogAlerts = doc.getRootElement().getAttributeValue(
				XML_ENABLE_LOG_ALERTS);
		if (enableLogAlerts == null)
			enableLogAlerts = "false";

		LOG.info("...ConfigLocation = [" + configFileLocation + "]");
		LOG.info("...Attempting to select live target: [" + liveTarget + "]");

		if (liveTarget == null || liveTarget.length() < 1)
			throw new IAMUnitException(
					"IAM00003: Live target profile not specified. Ensure that the idmunit root node in idmunit-config.xml contains a live-profile specification. The target must be defined in the same xml file along with its corresponding connections.");
		getProfiles(doc, connectionMap, liveTarget, enableEmailAlerts,
				enableLogAlerts);
		getParams(doc, parms, liveTarget);
		return connectionMap;
	}

	/**
	 * gets the profiles from the xml
	 * 
	 * @since v1.0.0
	 * @throws IAMUnitException
	 **/
	@SuppressWarnings("rawtypes")
	private static Map getProfiles(Document doc,
			Map<String, ConnectionConfigData> connectionMap, String liveTarget,
			String enableEmailAlerts, String enableLogAlerts)
			throws IAMUnitException {
		Iterator profileItr = doc.getRootElement().getChild(XML_PROFILES)
				.getChildren().iterator();
		while (profileItr.hasNext()) {
			Element profile = (Element) profileItr.next();
			String profileName = profile.getAttributeValue(XML_NAME);
			if (!liveTarget.equalsIgnoreCase(profileName)) {
				continue;
			}
			LOG.debug("...Selected profile: = [" + profileName + "]");
			Iterator connectionItr = profile.getChildren().iterator();
			while (connectionItr.hasNext()) {
				Element connection = (Element) connectionItr.next();
				String name = connection.getChildText(XML_NAME);
				String type = connection.getChildText(XML_TYPE);
				ConnectionConfigData configurationData = new ConnectionConfigData(
						name, type);

				for (Iterator i = connection.getChildren().iterator(); i
						.hasNext();) {
					Element param = (Element) i.next();
					if (param.getName().equals(XML_NAME)
							|| param.getName().equals(XML_SUBSTITUTIONS)
							|| param.getName().equals(XML_TYPE)) {
						continue;
					}
					if (param.getName().equals(XML_PASSWORD)) {
                		EncTool encryptionManager = new EncTool("IAM-UNIT");
                    	configurationData.setParam(XML_PASSWORD, encryptionManager.decryptCredentials(param.getText()));
                    	LOG.debug("...pwd: " + encryptionManager.decryptCredentials(param.getText()));
					} else {
						configurationData.setParam(param.getName(),
								param.getText());
					}
				}
				// Read Substitutions
				Map<String, String> substitutionMap = getSubstitutions(connection);

				if (type == null || type.length() < 1) {
					throw new IAMUnitException(
							"IAM00004: Missing target type- Please check the configuration file.");
				}
				if (substitutionMap != null && substitutionMap.size() > 0) {
					configurationData.setSubstitutions(substitutionMap);
				}

				connectionMap.put(name, configurationData);

				LOG.debug("...\tLoaded Profile: " + name);
			}
			// We don't need to process any more profiles, we found the live
			// profile and grabed the config data
			return connectionMap;
		}
		throw new IAMUnitException(
				"IAM00005: Live target profile not found. Ensure that the idmunit root node in idmunit-config.xml contains a VALID live-profile specification.");
	}

	/**
	 * reads the global parameters from xml file
	 * 
	 * @since v1.0.0
	 * @throws IAMUnitException
	 **/
	@SuppressWarnings("rawtypes")
	private static Map getParams(Document doc, Map<String, String> paramMap,
			String liveTarget) throws IAMUnitException {
		Iterator paramItr = doc.getRootElement().getChild(XML_PARAMS)
				.getChildren().iterator();
		while (paramItr.hasNext()) {
			while (paramItr.hasNext()) {
				Element param = (Element) paramItr.next();
				paramMap.put(param.getName(), param.getText());
			}
		}
		// Set Default values if not loaded from file.
		if (paramMap.get("multivaldelimiter") == null)
			paramMap.put("multivaldelimiter", "|");
		if (paramMap.get("defaultdelay") == null)
			paramMap.put("defaultdelay", "5000");
		if (paramMap.get("multiplierRetry") == null)
			paramMap.put("multiplierRetry", "0");
		if (paramMap.get("multiplierWait") == null)
			paramMap.put("multiplierWait", "0");
		if (paramMap.get("maxretries") == null)
			paramMap.put("maxretries", "10");
		// Return the parameter map
		return paramMap;
	}

	/**
	 * Retrieves configured connection substitution information
	 * 
	 * @since v1.0.0
	 * @param connection
	 *            The top level connection node
	 * @return Collection of substitutions
	 * @throws IAMUnitException 
	 */
	@SuppressWarnings("rawtypes")
	private static Map<String, String> getSubstitutions(Element connection) throws IAMUnitException {
		String replaceVal;
		String newVal;

		Iterator substitutionItr = connection.getChild(XML_SUBSTITUTIONS)
				.getChildren().iterator();
		Map<String, String> substitutionMap = new LinkedHashMap<String, String>();
		// FIXME: Check on <subtitutions/> tag
		while (substitutionItr.hasNext()) {
			Element substitution = (Element) substitutionItr.next();
			replaceVal = substitution.getChildText(XML_REPLACE);
			newVal = substitution.getChildText(XML_NEW);
			if (replaceVal != null && replaceVal.length() > 0 && newVal != null
					&& newVal.length() > 0) {
				LOG.debug("###\t\tValue to replace : " + replaceVal.toString());
				LOG.debug("###\t\tNew value        : " + newVal.toString());
				substitutionMap.put(replaceVal, newVal);
			}
		}
		return substitutionMap;
	}

	/**
	 * reads the setup tab in the sheet
	 * 
	 * @since v1.0.0
	 * @throws BiffException
	 * @throws IOException
	 * @throws IAMUnitException
	 **/
	public static void readConfig(String name) throws BiffException,
			IOException, IAMUnitException {
		String[] sheets;
		String sheetname = "setup";
		int sheetnumber = -1;
		// Setup the filename by getting the local dir
		File dir1 = new File(".");
		String fileName = dir1.getCanonicalPath()
				+ "\\tests\\com\\ictravi\\iamunit\\"
				+ ClassUtils.getShortClassName(name) + ".xls";
		Workbook workbook = null;

		try {
			// Try to read the filename
			workbook = Workbook.getWorkbook(new File(fileName));

			// Locate the setup worksheet
			for (int x = 0; x < workbook.getNumberOfSheets(); x++) {
				sheets = workbook.getSheetNames();
				if (sheets[x].equalsIgnoreCase(sheetname)) {
					sheetnumber = x;
					break;
				}
			}
			if (sheetnumber == -1)
				throw new IAMUnitException("IAM00006: No setup sheet used for this test");

			Sheet sheet = workbook.getSheet(sheetnumber);

			Cell[] cell;
			int rows = sheet.getRows();
			int i = 0;

			LOG.debug("...number of rows    : " + sheet.getRows());
			for (i = 0; i < rows; i++) {
				cell = sheet.getRow(i);
					LOG.debug("...first row (cell 0): " + cell[0].getContents()
							+ "\t\t(cell 1) " + cell[1].getContents());
			}
		} catch (Exception e) {
			throw new IAMUnitException("IAM00007: Sheet error: " + e.getMessage());
		} finally {
			if (workbook != null)
				workbook.close();
		}
	}

	/**
	 * reads the schema tab in the sheet
	 * 
	 * @since v1.0.0
	 * @throws BiffException
	 * @throws IOException
	 * @throws IAMUnitException
	 **/
	public static void readSchema(String name) throws BiffException,
			IOException, IAMUnitException {
		String[] sheets;
		String sheetname = "schema";
		int sheetnumber = -1;
		// Setup the filename by getting the local dir
		File dir1 = new File(".");
		String fileName = dir1.getCanonicalPath()
				+ "\\tests\\com\\ictravi\\iamunit\\"
				+ ClassUtils.getShortClassName(name) + ".xls";
		Workbook workbook = null;

		try {
			// Try to read the filename
			workbook = Workbook.getWorkbook(new File(fileName));

			// Locate the schema worksheet
			for (int x = 0; x < workbook.getNumberOfSheets(); x++) {
				sheets = workbook.getSheetNames();
				if (sheets[x].equalsIgnoreCase(sheetname)) {
					sheetnumber = x;
					break;
				}
			}
			if (sheetnumber == -1)
				throw new IAMUnitException("IAM00008: No schema sheet used for this test");
			Sheet sheet = workbook.getSheet(sheetnumber);
			Cell[] cell;
			int rows = sheet.getRows();
			int i = 0;

			LOG.debug("...number of rows    : " + sheet.getRows());
			for (i = 0; i < rows; i++) {
				cell = sheet.getRow(i);
				LOG.debug("...first row (cell 0): " + cell[0].getContents()
						+ "\t\t(cell 1) " + cell[1].getContents());
			}
		} catch (Exception e) {
			throw new IAMUnitException("IAM00009: Sheet error: " + e.getMessage());
		} finally {
			if (workbook != null)
				workbook.close();
		}
	}

}
